#include "debug_logger.h"
#include "Extern_global_Variable.h"

String ScriptDataValueToString(PInstance Instance, PScriptData Data)
{
	String Result;
	Result = new char[100];
	switch ((*Data).InitializedType)
	{
		case stVoid: sprintf(Result,"nothing"); break;
		case stRetAddr: sprintf(Result,"return address %X", (*Data).Value); break;
		case stNull: sprintf(Result,"null"); break;
		case stCode: sprintf(Result,"function %s", GetCodeName2(
			Instance,
			PInstruction((*Data).Value)
		)); break;
		case stInteger: sprintf(Result,"%d",*(PInt32(&(*Data).Value))); break; //Result:= 'int';
		case stReal: sprintf(Result,"%f",*(PSingle(&(*Data).Value))); break; //Result:= 'real';
		case stString: sprintf(Result,"""%s""", (*Instance).StringTable.GetItem((*Data).Value).GetContent()); break;
		case stHandle: sprintf(Result,"0x%x", (*Data).Value); break;
		case stBoolean: sprintf(Result,"%s", LongBool((*Data).Value) ? "true" : "false"); break;
		case stArrayOfInteger:
		case stArrayOfReal:
		case stArrayOfString:
		case stArrayOfHandle:
		case stArrayOfBoolean: sprintf(Result,"array at 0x%.8X", (*Data).Value); break;
		default: sprintf(Result,"unknown(0x%X)", (*Data).Value); break;
	};
	return Result;
};

/*procedure FormatArguments(PInstance Instance, PPScriptData LastArgument, Dword hName, TAnsiStringBuilderHack AStringBuilder)
{
	TScriptHandle hScript;
	TFuncTable Functions;
	Integer Count;
	Integer vvi;
	PPScriptData FirstArgument;
	String vStr;

	hScript = GetInstanceScriptHandle(Instance);
	Functions = GetScriptFunctions(hScript);
	Count = Functions[hName].ArgsCount;
	FirstArgument = LastArgument - (Count - 1);

	AStringBuilder.Append("(");
	for (vvi = 0; vvi <= Count - 1; vvi++)
	{
		vStr = ScriptDataValueToString(Instance, FirstArgument[i]);
		AStringBuilder.Append(vStr);

		if (i < (Count - 1))
		{
			AStringBuilder.Append(", ");
		}
	};
	AStringBuilder.Append(")");
};*/

/*procedure __stdcall OnCall(PInstance Instance, Dword hName)
{
	PChar Name;
	TFunctionTable LoggedFunctions;
	PPScriptData Arguments;
	TScriptHandle hScript;

	if (IgnoreLevel == 0)
	{
		hScript = GetInstanceScriptHandle(Instance);
		LoggedFunctions = FunctionTables.GetValue(hScript);

		if (LoggedFunctions[hName])
		{
			Name = (*(*Instance).Script).Strings.GetItem(hName);
			StringBuilder.Length = 0;
			StringBuilder.Append(Name);

			// The new frame has not yet been created, so look for arguments in the current one.
			// The return address has not yet been pushed, so don't skip them.
				Arguments = &(*(*Instance).Frames.First).Stack.GetItem((*(*Instance).Frames.First).Stack.GetLength() - 1);

			FormatArguments(Instance, Arguments, hName, StringBuilder);
			Logger.DebugLnEnter(&StringBuilder.Data[0], StringBuilder.Length);
		}
		else
		{
			IgnoreLevel++;
		}
	}
	else
	{
		IgnoreLevel++;
	};
};*/

/*procedure __stdcall OnNativeCall(PInstance Instance, Dword hName)
{
	PChar Name;
	PPScriptData Arguments;

	Name = (*(*Instance).Script).Strings.GetItem(hName);
	StringBuilder.Length = 0;
	StringBuilder.Append(Name);

		Arguments = &(*(*Instance).Frames.First).Stack.GetItem((*(*Instance).Frames.First).Stack.GetLength() - 1);

	FormatArguments(Instance, Arguments, hName, StringBuilder);
	Logger.DebugLn(&StringBuilder.Data[0], StringBuilder.Length);
};*/

/*procedure op_call_jump_case()
{
const Integer OP_CALL_JUMP_CASE = Integer(opCall) - 2;
__asm
{
	pushad
	push eax
	push esi
	call OnCall
	popad
	mov ecx, OP_CALL_JUMP_CASE
	jmp JumpTableCopy[ecx*4]
};
};*/

procedure __stdcall OnReturn(PInstance Instance)
{
	PFrame Frame;
	Boolean IsLastFrame;

	Frame = (*Instance).Frames.GetFirst();
	if (Frame != NULL)
	{
		IsLastFrame = (*Instance).Frames.GetNextItem(Frame);
		if (!(*Frame).IsFirstFrame && !IsLastFrame)
		{
			if (IgnoreLevel == 0)
			{
				Logger.DebugLnExit();
			}
			else
			{
				IgnoreLevel--;
			}
		};
	};
};

procedure op_return_jump_case()
{
const Integer OP_RETURN_JUMP_CASE = Integer(opReturn) - 2;
__asm
{
	push esi
	call OnReturn
	mov ecx, OP_RETURN_JUMP_CASE
	jmp JumpTableCopy[ecx*4]
};
};

Integer OperationCaseIndex(TOperationCode id)
{
	return Integer(id) - 2;
};

procedure HookInstruction(TOperationCode id, Pointer Target)
{
	if (!WriteProcessMemory(
		GetCurrentProcess(),
		&_pJumpTable[OperationCaseIndex(id)],
		&Target,
		sizeof(Target),
		NULL
	))
	{
		Logger.DebugLn("Failed to hook instruction %d.", id);
	}
};

procedure PatchTable()
{
    memcpy(&JumpTableCopy, _pJumpTable, sizeof(TJumpTable));
	//HookInstruction(opCall, &op_call_jump_case);
	HookInstruction(opReturn, &op_return_jump_case);
};

/*TFunctionTable GenerateFunctionTable(TScriptHandle hScript)
{
	Integer vvi;
	TFunctionTable* Result;
	PJassScript pScript;
	String Name;
	Dword Index;

	pScript = GetJassThreadLocal()->Scripts.GetItem(hScript);
	Result = new TFunctionTable[(*pScript).Strings->GetLength()];
	memset(Result,Byte(TRUE),(sizeof(*Result)/sizeof(Result[0])-1) * sizeof(Boolean));

	for (vvi = 0; vvi <= 1; vvi++)
	{
		Name = Blacklist[vvi];
		if ((*pScript).Strings->GetItemIndex(PChar(Name), Index))
		{
			Result[Index] = FALSE;
		};
	};
	return *Result;
};*/

/*procedure InitScriptFunctionsTable(TScriptHandle hScript)
{
	TFunctionTable Table;

	Table = GenerateFunctionTable(hScript);
	FunctionTables.InsertValue(hScript, Table);
};*/

/*TScriptHandle __stdcall hook_Compile(Dword ErrorCallbackParam, Dword RiskLevel) // fastcall
{
	TScriptHandle Result;
	Result = orig_Compile(ErrorCallbackParam, RiskLevel);
	InitScriptFunctionsTable(Result);
	return Result;
};*/

/*Boolean FilterCommentedEntries(TObject Sender, const String s)
{
	return s.StartsWith('#');
};*/

/*TMethod ToObjectProcedure(Pointer Callback)
{
	TMethod Result;
	Result.Code = Callback;
	Result.Data = NULL;
	return Result;
};*/

//Boolean (*TStringFilterFunction) (TObject Sender, const String s);

/*TStringsFilterMethod AsStringFilter(TStringFilterFunction Callback)
{
	return TStringsFilterMethod(ToObjectProcedure(Callback));
};*/

/*procedure LoadBlacklist()
{
	try
	{
		Blacklist.LoadFromFile(DEFAULT_BLACKLIST_PATH);
	}
	catch(...)
	{
		EFOpenError e;
		Blacklist.Clear();
	};
	Blacklist.Filter(AsStringFilter(&FilterCommentedEntries));
};*/

Boolean ExtractParameter(String Text, String Name, String &Value)
{
	Boolean Result;
	Result = FALSE;

	Value = new char[100];

	if (1)//Text.StartsWith(Name, True))
	{
		sprintf(Value,"%s",Text);
		//Value = Copy(Text, strlen(Name) + 1, strlen(Text));
		Result = TRUE;
	};
	return Result;
};

/*procedure ParseCommandLine()
{
	Integer vvi;
	String Value;

	for (vvi = 1; vvi <= ParamCount; vvi++)
	{
		if (ExtractParameter(ParamStr(vvi), "--jass-logger-file=", Value))
		{
			LogFilePath = Value;
		}
	};
};*/

/*procedure debug_logger_OnInit()
{
	Pointer(_pJumpTable) = Pointer((HMODULE)GameDll + 0x45F808);
	//PatchTable;

	if (!Hook(
		Pointer(_Compile),
		&hook_Compile,
		Pointer(orig_Compile),
		NULL
	))
	{
		Logger.DebugLn("Failed to hook compiler.");
	}
};*/

/*void debug_logger_main()
{
	wc3main_SubscribeOnInit(&debug_logger_OnInit);
	ParseCommandLine();
	Logger.TLogger::TLogger(LogFilePath);

	//Blacklist = TStringList.Create;
	//FunctionTables = TFunctionTables.Create;
	LoadBlacklist();

	//StringBuilder = TAnsiStringBuilderHack.Create;
};*/

